package org.gongliang.config;


import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.gongliang.common.shiro.HttpSessionManager;
import org.gongliang.common.shiro.RetryLimitCredentialsMatcher;
import org.gongliang.common.shiro.ShiroDbRealm;
import org.gongliang.common.shiro.cache.ShiroSpringCacheManager;
import org.gongliang.common.shiro.filter.ForceLogoutFilter;
import org.gongliang.common.shiro.filter.UserAccessFilter;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import com.google.common.collect.Maps;

@Configuration()
public class ShiroConfig {

	/**
	 * 安全管理器
	 */
	@Bean(name = "securityManager")
	public org.apache.shiro.mgt.SecurityManager securityManager(ShiroDbRealm shiroDbRealm,
			CookieRememberMeManager rememberMeManager, org.apache.shiro.cache.CacheManager cacheManager,
			SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(shiroDbRealm);
		securityManager.setCacheManager(cacheManager);
		securityManager.setRememberMeManager(rememberMeManager);
		securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

	/**
	 * 项目自定义的Realm
	 */
	@Bean(name = "shiroDbRealm")
	public ShiroDbRealm shiroDbRealm(org.apache.shiro.cache.CacheManager cacheManager,
			CredentialsMatcher credentialsMatcher) {
		ShiroDbRealm shiroDbRealm = new ShiroDbRealm(cacheManager, credentialsMatcher);
		shiroDbRealm.setCachingEnabled(true);
		shiroDbRealm.setAuthenticationCachingEnabled(false);
		shiroDbRealm.setAuthorizationCachingEnabled(false);
		shiroDbRealm.setAuthenticationCacheName("userinfoCache");
		shiroDbRealm.setAuthorizationCacheName("permissionCache");
		return shiroDbRealm;
	}
	
	/**
	 * 记住密码Cookie
	 */
	@Bean(name = "rememberMeCookie")
	public SimpleCookie rememberMeCookie() {
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setPath("/");
		simpleCookie.setMaxAge(7 * 24 * 60 * 60);// 7天
		return simpleCookie;
	}
	
	/**
	 * rememberMe管理器, cipherKey生成见{@code Base64Test.java}
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {
		CookieRememberMeManager manager = new CookieRememberMeManager();
		manager.setCipherKey(Base64.decode("Z3VucwACAOVAKALACAADSA=="));
		manager.setCookie(rememberMeCookie);
		return manager;
	}

	/**
	 * session管理器
	 */
	@Bean(name = "sessionManager")
	public SessionManager sessionManager(SessionDAO sessionDao, SimpleCookie sessionIdCookie) {
		HttpSessionManager sessionManager = new HttpSessionManager();
		sessionManager.setGlobalSessionTimeout(30 * 60 * 1000);
		sessionManager.setSessionIdUrlRewritingEnabled(false);
		sessionManager.setDeleteInvalidSessions(true);
		sessionManager.setSessionIdCookieEnabled(true);
		sessionManager.setSessionValidationSchedulerEnabled(true);
		sessionManager.setSessionIdCookie(sessionIdCookie);
		sessionManager.setSessionDAO(sessionDao);
		return sessionManager;
	}

	@Bean(name = "sessionDAO")
	public SessionDAO sessionDAO(org.apache.shiro.cache.CacheManager cacheManager) {
		EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
		sessionDAO.setActiveSessionsCacheName("activeSessionCache");
		sessionDAO.setCacheManager(cacheManager);
		return sessionDAO;
	}

    /**
	 * redis缓存管理器
	 */

	@Bean(name = "shiroSpringCacheManager")
	public org.apache.shiro.cache.CacheManager shiroSpringCacheManager(CacheManager redisCacheManager) {
		ShiroSpringCacheManager cacheManager = new ShiroSpringCacheManager();
		cacheManager.setCacheManager(redisCacheManager);
		return cacheManager;
	}


	/**
	 * 缓存管理器 使用Ehcache实现
	 */
	// @Bean
	// public org.apache.shiro.cache.CacheManager getCacheShiroManager() {
	// EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new
	// EhCacheManagerFactoryBean();
	// ehCacheManagerFactoryBean.setConfigLocation(new
	// ClassPathResource("ehcache.xml"));
	// EhCacheManager ehCacheManager = new EhCacheManager();
	// ehCacheManager.setCacheManager(ehCacheManagerFactoryBean.getObject());
	// return ehCacheManager;
	// }


	/**
	 * 错误5次锁定
	 */
	@Bean(name = "credentialsMatcher")
	public CredentialsMatcher credentialsMatcher(org.apache.shiro.cache.CacheManager cacheManager) {
		RetryLimitCredentialsMatcher hashedCredentialsMatcher = new RetryLimitCredentialsMatcher(cacheManager);
		hashedCredentialsMatcher.setHashIterations(2);
		hashedCredentialsMatcher.setHashAlgorithmName("md5");
		return hashedCredentialsMatcher;
	}


	@Bean(name = "sessionIdCookie")
	public SimpleCookie sessionIdCookie() {
		SimpleCookie sessionIdCookie = new SimpleCookie("x-auth-token");
		sessionIdCookie.setHttpOnly(true);
		sessionIdCookie.setMaxAge(-1);// 关闭浏览器
		sessionIdCookie.setPath("/");
		return sessionIdCookie;
	}


    /**
     * Shiro的过滤器链
     */
    @Bean
	public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        Map<String, Filter> filters =Maps.newHashMap();
		filters.put("forceLogout", new ForceLogoutFilter());
		LogoutFilter logout = new LogoutFilter();
		logout.setRedirectUrl("/sys/login");
		filters.put("logout", logout);
		filters.put("userAccess", new UserAccessFilter());
        shiroFilter.setFilters(filters);
        /**
         * 默认的登陆访问url
         */
		shiroFilter.setLoginUrl("/sys/login");
        /**
         * 登陆成功后跳转的url
         */
        shiroFilter.setSuccessUrl("/");
        /**
         * 没有权限跳转的url
          */
		shiroFilter.setUnauthorizedUrl("/sys/unauth");
        /**
         * 配置shiro拦截器链
         *
         * anon  不需要认证
         * authc 需要认证
         * user  验证通过或RememberMe登录的都可以
         *
         */
        Map<String, String> hashMap =Maps.newHashMap();
        hashMap.put("/public/**", "anon");
		hashMap.put("/sys/login", "anon");
		hashMap.put("/sys/logout", "logout");
		hashMap.put("/**", "user,perms,roles,forceLogout");
        shiroFilter.setFilterChainDefinitionMap(hashMap);
        return shiroFilter;
    }

	/**
	 * 在方法中 注入 securityManager,进行代理控制
	 */
	@Bean
	public MethodInvokingFactoryBean methodInvokingFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
		MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
		bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
		bean.setArguments(new Object[] { securityManager });
		return bean;
	}

    /**
     * 保证实现了Shiro内部lifecycle函数的bean执行
     */
	@Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


	/**
	 * 启用shrio授权注解拦截方式，AOP式方法级权限检查
	 */
	@Bean
	@DependsOn(value = "lifecycleBeanPostProcessor") // 依赖其他bean的初始化
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
		return defaultAdvisorAutoProxyCreator;
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
			org.apache.shiro.mgt.SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}




}
